﻿#include "OiPCH.hpp"
#include "Tools/OiPlyTool.hpp"
#include <QJsonDocument>
#include "Util/OiOPCTask.hpp"

namespace Oi { namespace PLScannerCore {

    class ParamsReaderSystemImpl : public Impl<ParamsReaderSystem>
    {
    public:
        QMap<QString, QSharedPointer<ParamsReader>> _readers;
    };

    ProductParams::ProductParams()
    {
        specName = "Default";
        materialsType = MT_CALENDER;
        materialsWidth = 100.0;
        materialsUpWidth = 0;
        materialsThickness = 1.0;
        measureType = 0;

        insParams[TT_TotolWidth] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_TotolWidth));
        insParams[TT_Precision] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_Precision));
        insParams[TT_LeftSide] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_LeftSide));
        insParams[TT_RightSide] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_RightSide));
        config = SettingsDlg::config();
    }

    ProductParams::ProductParams(const ProductParams& p)
    {
        *this = p;
    }

    ProductParams::ProductParams(const int mtype, const QString mSpec, const double mWidth, const double mUpWidth, const double mThickness, const int mMeasureType
        , const QList<DefinitionPoint> mdefinitionPoints 
        , const QList<QPointF> mmeasureThickness 
        , const QPointF mfixedPoint
        , const OiProfiles mprofiles
        , const QMap<int, QVariant> mInsParams)
    {
        if (mWidth <= 0 || mThickness <= 0 || mSpec.isEmpty())
        {
            return;
        }

        specName = mSpec;
        materialsType = mtype;
        materialsWidth = mWidth;
        materialsWidth = mUpWidth;
        materialsThickness = mThickness;
        definitionPoints = mdefinitionPoints;
        measureThickness = mmeasureThickness;
        fixedPoint = mfixedPoint;
        profiles = mprofiles;
        measureType = mMeasureType;

        if (mInsParams.isEmpty())
        {
            if (materialsType == MT_CALENDER)
            {
                insParams[TT_TotolWidth] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_TotolWidth));
                insParams[TT_LeftSide] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_LeftSide));
                insParams[TT_RightSide] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_RightSide));
                insParams[TT_Precision] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_TotolWidth));

                for (int i = 0; i < definitionPoints.size(); i++)
                {
                    insParams[TT_DefinitionTool + i] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_DefinitionTool));
                }

                for (int i = 0; i < measureThickness.size(); i++)
                {
                    insParams[TT_MeasureThicknessTool + i] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_MeasureThicknessTool));
                }

                insParams[TT_FixedPointMeasure] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_FixedPointMeasure));
            }
        }
        else
        {
            insParams = mInsParams;
        }

        config = SettingsDlg::config();
    }

    ProductParams::ProductParams(const QString mSpec, const double mWidth, const double mUpWidth, const double mThickness, const int mMeasureType
        , const QList<DefinitionPoint> mdefinitionPoints
        , const QList<double> mmeasureThicknessPos
        , const double mfixedPos
        , const double mWidthErrTol
        , const double mWidthWarnTol
        , const double mThicknessErrTol
        , const double mThicknessWarnTol)
    {
        if (mWidth <= 0 || mThickness <= 0 || mSpec.isEmpty())
        {
            return;
        }

        specName = mSpec;
        materialsType = MT_CALENDER;
        materialsWidth = mWidth;
        materialsUpWidth = mUpWidth;
        materialsThickness = mThickness;
        definitionPoints = mdefinitionPoints;
        profiles = createProfile(mWidth, mThickness, mdefinitionPoints);
        measureThickness.clear();
        fixedPoint = QPointF();
        measureType = mMeasureType;

        if (profiles.size() > 0)
        {
            for (auto mtp : mmeasureThicknessPos)
            {
                auto y = PLScannerUtil::y_at_profile(profiles[0], mtp);
                if (y > 0)
                {
                    measureThickness.append(QPointF(mtp, y));
                }
            }

            auto fy = PLScannerUtil::y_at_profile(profiles[0], mfixedPos);

            if (fy > 0)
            {
                fixedPoint = QPointF(mfixedPos, fy);
            }
        }
       
        if (materialsType == MT_CALENDER)
        {
            auto& wp = PlyTool::toolDefaultValues(TT_TotolWidth);
            auto wpv = wp["x"].value<QVariantMap>();
            wpv["WarningTol"] = mWidthWarnTol;
            wpv["ErrorTol"] = mWidthErrTol;
            wp["x"] = wpv;

            auto& mtp = PlyTool::toolDefaultValues(TT_MeasureThicknessTool);
            auto mtpv = mtp["y"].value<QVariantMap>();
            mtpv["WarningTol"] = mThicknessWarnTol;
            mtpv["ErrorTol"] = mThicknessErrTol;
            mtp["y"] = mtpv;
//             auto& ftp = PlyTool::toolDefaultValues(TT_FixedPointMeasure)["y"].value<QVariantMap>();
//             mtp["WarningTol"] = mThicknessWarnTol;
//             mtp["ErrorTol"] = mThicknessErrTol;

            insParams[TT_TotolWidth] = QVariant::fromValue(wp);
            insParams[TT_LeftSide] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_LeftSide));
            insParams[TT_RightSide] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_RightSide));

            for (int i = 0; i < definitionPoints.size(); i++)
            {
                insParams[TT_DefinitionTool + i] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_DefinitionTool));
            }

            for (int i = 0; i < measureThickness.size(); i++)
            {
                insParams[TT_MeasureThicknessTool + i] = QVariant::fromValue(mtp);
            }

            insParams[TT_FixedPointMeasure] = QVariant::fromValue(mtp);
        }

    }

    bool ProductParams::getStdValue(int index, QString key, double& value)
    {
        bool ret = false;

        value = 0.0;

        if (index == TT_TotolWidth)
        {
            value = materialsWidth;
            ret = true;
        }
        else if (index == TT_LeftSide)
        {
            if (key == "x")
            {
                value = -0.5 * materialsWidth;
                ret = true;
            }
            else if (key == "y")
            {
                value = materialsThickness;
                ret = true;
            }
        }
        else if (index == TT_RightSide)
        {
            if (key == "x")
            {
                value = +0.5 * materialsWidth;
                ret = true;
            }
            else if (key == "y")
            {
                value = materialsThickness;
                ret = true;
            }
        }
        else if (index >= TT_DefinitionTool && index < TT_MeasureThicknessTool)
        {
            if (definitionPoints.size() > index - TT_DefinitionTool)
            {
                auto p = definitionPoints[index - TT_DefinitionTool];

                if (key == "x")
                {
                    value = p.distance;
                    ret = true;
                }
                else if (key == "y")
                {
                    value = p.thicknessLeft;
                    ret = true;
                }
            }
        }
        else if (index >= TT_MeasureThicknessTool && index < TT_FixedPointMeasure)
        {
            if (measureThickness.size() > index - TT_MeasureThicknessTool)
            {
                auto p = measureThickness[index - TT_MeasureThicknessTool];

                if (key == "x")
                {
                    value = p.x();
                    ret = true;
                }
                else if (key == "y")
                {
                    value = p.y();
                    ret = true;
                }
            }
        }
        else if (index == TT_FixedPointMeasure)
        {
            if (key == "x")
            {
                value = fixedPoint.x();
                ret = true;
            }
            else if (key == "y")
            {
                value = fixedPoint.y();
                ret = true;
            }
        }

        return ret;
    }

    bool ProductParams::getMeasureEnable(int index, QString key, int& value)
    {
        bool ret = false;
        value = 0;

        if (insParams.contains(index))
        {
            auto vm = insParams[index].value<QVariantMap>();

            if (vm.contains(key))
            {
                auto limit = vm[key].value<QVariantMap>();
                if (limit.contains("Enable"))
                {
                    value = limit["Enable"].toInt();
                    ret = true;
                }
            }
        }

        return ret;
    }

    bool ProductParams::getMeasureWarningTol(int index, QString key, double &value)
    {
        bool ret = false;
        value = 0;

        if (insParams.contains(index))
        {
            auto vm = insParams[index].value<QVariantMap>();

            if (vm.contains(key))
            {
                auto limit = vm[key].value<QVariantMap>();
                if (limit.contains("WarningTol"))
                {
                    value = limit["WarningTol"].toDouble();
                    ret = true;
                }
            }
        }

        return ret;
    }

    bool ProductParams::getMeasureErrorTol(int index, QString key, double& value)
    {
        bool ret = false;
        value = 0;

        if (insParams.contains(index))
        {
            auto vm = insParams[index].value<QVariantMap>();

            if (vm.contains(key))
            {
                auto limit = vm[key].value<QVariantMap>();
                if (limit.contains("ErrorTol"))
                {
                    value = limit["ErrorTol"].toDouble();
                    ret = true;
                }
            }
        }

        return ret;
    }
    void ProductParams::getMeasurePrecision(int& value)
    {
        value = Core::OiSettings()->value("Measure/precision",2).toInt();
    }
    void ProductParams::getMeasureEdgeThickness(double& thickness)
    {
        QDir tempDir;
        QString tempFilePath = tempDir.currentPath() + "\\settings\\" + "formual.ini";  // currentPath()方法返回应用程序所在的当前目录；

        QSettings settings(tempFilePath, QSettings::Format::IniFormat);
        thickness = settings.value(QString("%1").arg(current()) + "/ThicknessValue", 0.00).toDouble();
        if (thickness == 0.00)
        {
            thickness = Core::OiSettings()->value("Measure/EdgeThickness", 0.003).toDouble();
        }
        
    }
    ProductParams& ProductParams::operator=(const ProductParams& p)
    {
        specName = p.specName;
        materialsType = p.materialsType;
        materialsWidth = p.materialsWidth;
        materialsUpWidth = p.materialsUpWidth;
        materialsThickness = p.materialsThickness;
        definitionPoints = p.definitionPoints;
        measureThickness = p.measureThickness;
        fixedPoint = p.fixedPoint;
        measureType = p.measureType;

        insParams = p.insParams;
        profiles = p.profiles;
        config = p.config;

        return *this;
    }

    bool ProductParams::operator==(const ProductParams &p) const
    {
        if (specName != p.specName)
        {
            return false;
        }

        if (materialsType != p.materialsType)
        {
            return false;
        }

        if (materialsWidth != p.materialsWidth)
        {
            return false;
        }
        if (materialsUpWidth != p.materialsUpWidth)
        {
            return false;
        }

        if (materialsThickness != p.materialsThickness)
        {
            return false;
        }

        if (definitionPoints != p.definitionPoints)
        {
            return false;
        }

        if (measureThickness != p.measureThickness)
        {
            return false;
        }

        if (profiles != p.profiles)
        {
            return false;
        }

        if (insParams != p.insParams)
        {
            return false;
        }

        if (config != p.config)
        {
            return false;
        }

        if (fixedPoint != p.fixedPoint)
        {
            return false;
        }

        if (measureType != p.measureType)
        {
            return false;
        }

        return true;
    }

    bool ProductParams::operator!=(const ProductParams &p) const
    {
        return !(*this == p);
    }

    OiProfiles ProductParams::createProfile(double width, const double thickness, const QList<DefinitionPoint> definitionPoints)
    {
        OiProfiles profiles;
        QPainterPath path;
        path.moveTo(-width / 2, 0);
        path.lineTo(-width / 2, thickness);

        QList<DefinitionPoint> mdefinitionPoints = definitionPoints;

        std::sort(mdefinitionPoints.begin(), mdefinitionPoints.end(), [](const DefinitionPoint& p1, const DefinitionPoint& p2) {return p1.distance < p2.distance; });

        QList< QPointF> corner;

        for (int i = mdefinitionPoints.size() - 1; i >= 0; i--)
        {
            auto p = mdefinitionPoints[i];
            if (fabs(p.distance) >= width / 2)
            {
                continue;
            }
            if (p.type & DT_FullWidth)
            {
                if (p.thicknessLeft > 0.0)
                {
                    if (p.type & DT_Mutational)
                    {
                        corner.append(QPointF(-p.distance, -1.0));
                        corner.append(QPointF(-p.distance, p.thicknessLeft));
                    }
                    else
                    {
                        corner.append(QPointF( -p.distance, p.thicknessLeft));
                    }
                }
            }
            else if (mdefinitionPoints[i].type & DT_HalfWidth)
            {
                if (p.type & DT_Mutational)
                {
                    corner.append(QPointF(-p.distance, -1.0));
                    corner.append(QPointF(-p.distance, p.thicknessLeft));
                }
                else
                {
                    corner.append(QPointF(-p.distance, p.thicknessLeft));
                }
            }
        }

        for (int i = 0; i < mdefinitionPoints.size(); i++)
        {
            auto p = mdefinitionPoints[i];

            if (fabs(p.distance) >= width / 2)
            {
                continue;
            }
            if (p.type & DT_FullWidth)
            {
                if (p.thicknessRight > 0.0)
                {
                    if (p.type & DT_Mutational)
                    {
                        corner.append(QPointF(+p.distance, p.thicknessRight));
                        corner.append(QPointF(+p.distance, -2.0));
                    }
                    else
                    {
                        corner.append(QPointF(+p.distance, p.thicknessRight));
                    }
                }
            }
            else if (mdefinitionPoints[i].type & DT_HalfWidth)
            {
                if (p.type & DT_Mutational)
                {
                    corner.append(QPointF(+p.distance, p.thicknessLeft));
                    corner.append(QPointF(+p.distance, -2.0));
                }
                else
                {
                    corner.append(QPointF(+p.distance, p.thicknessLeft));
                }
            }
        }

        for (int i = 0; i < corner.size(); i++)
        {
            if (corner[i].y() == -1.0)
            {
                if (i > 0)
                {
                    corner[i].setY(corner[i - 1].y());
                }
                else
                {
                    corner[i].setY(thickness);
                }
            }
            else if (corner[i].y() == -2.0)
            {
                if (i < corner.size() - 1)
                {
                    corner[i].setY(corner[i + 1].y());
                }
                else
                {
                    corner[i].setY(thickness);
                }
            }
        }

        //std::sort(mdefinitionPoints.begin(), mdefinitionPoints.end(), [](const DefinitionPoint& p1, const DefinitionPoint& p2) {return p1.distance < p2.distance; });

        for (int i = 0; i < corner.size(); i++)
        {
            path.lineTo(corner[i]);
        }

        path.lineTo(+width / 2, thickness);
        path.lineTo(+width / 2, 0);

        auto polygons = path.toSubpathPolygons();
        for (auto polygon : polygons)
        {
            profiles.push_back(polygon.toList());
        }

        return profiles;
    }

    void ProductParams::setDefinitionPoints(const QList<DefinitionPoint> mdefinitionPoints)
    {
        definitionPoints = mdefinitionPoints;

        OiProfiles mprofiles;
        mprofiles = createProfile(materialsWidth, materialsThickness, mdefinitionPoints);
        profiles = mprofiles;
    }
  
    ProductParams ProductParams::params(int& success)
    {
        success = 0;
        auto name = Core::OiSettings()->value("Product/Current").toString();

        ProductParams params;

        if(name.isEmpty())
        {
            success = 1;
            return params;
        }
        else
        {
            if (!ProductParams::loadFromLocal(params, name))
            {
                success = 2;
            }

            return params;
        }
    }

    QString ProductParams::current()
    {
        return Core::OiSettings()->value("Product/Current").toString();
    }

    QString ProductParams::next()
    {
        return Core::OiSettings()->value("Product/Next").toString();
    }

    void ProductParams::setCurrent(const QString name)
    {
        Core::OiSettings()->setValue("Product/Current", name);
        Core::OiSettings()->sync();
    }

    void ProductParams::setNext(const QString name)
    {
        Core::OiSettings()->setValue("Product/Next", name);
        Core::OiSettings()->sync();
    }

    bool ProductParams::checkName(const QString name)
    {
        auto fileName = PLScanner::instance()->productDir + "/" + name + ".plsp";

        return QFileInfo::exists(fileName);
    }

    int ProductParams::loadFromJson(const QString file)
    {
        int ret = 0;

        if (!QFileInfo::exists(file))
        {
            return ret;
        }

        QFile jsonReadFile(file);

        QByteArray bytes;

        if (jsonReadFile.open(QFile::ReadOnly))
        {
            bytes = jsonReadFile.readAll();
            jsonReadFile.close();
        }
        else
        {
            return ret;
        }

        QJsonParseError jsonError;

        QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError);

        if (doucment.isNull() || jsonError.error != QJsonParseError::NoError)
            return ret;

        if (doucment.isArray())
        {
            auto ms = doucment.array();
            for (auto& m : ms)
            {
                auto mo = m.toObject();
                if (mo.contains("Spec"))
                {
                    QString spec = mo["Spec"].toString();
                    double width = mo["Width"].toDouble();
                    double upWidth = mo["UpWidth"].toDouble();
                    double thickness = mo["Thickness"].toDouble();
                    double widthET = mo["WidthErrorTol"].toDouble();
                    double widthWT = mo["WidthWarningTol"].toDouble();
                    double thicknessET = mo["ThicknessErrorTol"].toDouble();
                    double thicknessWT = mo["ThicknessWarningTol"].toDouble();
                    int measureType = mo["MeasureType"].toInt();
                    double fixedMeaurePos = mo["FixedMeasurePos"].toDouble();

                    auto dpjs = mo["DefinitionPoint"].toArray();

                    QList<DefinitionPoint> dps;
                    for (auto& dpj : dpjs)
                    {
                        auto dp = dpj.toObject();
                        DefinitionPoint p;

                        p.type = dp["Type"].toInt();
                        p.distance = dp["Distance"].toDouble();
                        p.thicknessLeft = dp["ThicknessLeft"].toDouble();
                        p.thicknessRight = dp["ThicknessRight"].toDouble();

                        dps.push_back(p);
                    }

                    auto mpjs = mo["MeasurePoint"].toArray();
                    QList<double> mps;

                    for (auto& mp : mpjs)
                    {
                        mps.append(mp.toDouble());
                    }

                    ProductParams nParams(spec, width, upWidth, thickness, measureType, dps, mps, fixedMeaurePos, widthET, widthWT, thicknessET, thicknessWT);

                    if (saveToLocal(nParams))
                    {
                        ret++;
                    }
                }
            }
        }

        return ret;
    }

    bool ProductParams::loadFromLocal(ProductParams& params, const QString name, const QString path)
    {
        QString fileName = path;

        if (fileName.isEmpty())
        {
            fileName = PLScanner::instance()->productDir + "/" + name + ".plsp";
        }

        if(!QFileInfo::exists(fileName))
        {
            return false;
        }
       
        Core::ZipReader reader(fileName);

        if(reader.result() != 0)
        {
            return false;
        }

        if (reader.hasFile("params.dat"))
        {
            auto data = reader.fileData("params.dat");
            QDataStream stream(&data, QIODevice::ReadOnly);
            stream >> params.specName;
            stream >> params.materialsType;
            stream >> params.materialsWidth;
            stream >> params.materialsUpWidth;
            stream >> params.materialsThickness;
            stream >> params.definitionPoints;
            stream >> params.measureThickness;
            stream >> params.fixedPoint;
            stream >> params.measureType;
        }

        if(reader.hasFile("profile.dat"))
        {
            auto data = reader.fileData("profile.dat");
            QDataStream stream(&data, QIODevice::ReadOnly);

            if(!stream.atEnd())
            {
                OiProfiles profile;
                stream >> profile;

                params.profiles = profile;
            }
        }
        else
        {
            params.profiles = createProfile(params.materialsWidth, params.materialsThickness, params.definitionPoints);
        }

        if (params.fixedPoint.isNull())
        {
            if (params.profiles.size() > 0)
            {
                params.fixedPoint = QPointF(0.0, PLScannerUtil::y_at_profile(params.profiles[0], 0.0));
            }
        }

        if(reader.hasFile("config.dat"))
        {
            auto data = reader.fileData("config.dat");
            QDataStream stream(&data, QIODevice::ReadOnly);

            if(!stream.atEnd())
            {
                QMap<int, QVariant> insParams;
                QVariantMap  config;
                stream >> insParams;
                stream >> config;
                params.insParams = insParams;
                params.config = config;
            }
        }

        if (!params.insParams.contains(TT_FixedPointMeasure))
        {
            params.insParams[TT_FixedPointMeasure] = QVariant::fromValue(PlyTool::toolDefaultValues(TT_FixedPointMeasure));
        }

        return true;
    }

    bool ProductParams::loadFromSys(ProductParams& params, const QString name)
    {
        auto reader = ParamsReaderSystem::instance()->reader(name);

        if(reader)
        {
            return reader->params(params);
        }
        else
        {
            return false;
        }
    }

    bool ProductParams::loadFromSysAndLocal(ProductParams& params, const QString name)
    {
        auto reader = ParamsReaderSystem::instance()->reader(name);

        if (reader)
        {
            QString spec;
            bool ret = reader->specName(spec);

            if (ret)
            {
                ret = loadFromLocal(params, spec);
            }

            return ret;
        }
        else
        {
            return false;
        }
    }

    bool ProductParams::loadNameFromSys(QString& spec, const QString name)
    {
        auto reader = ParamsReaderSystem::instance()->reader(name);

        if (reader)
        {
            return reader->specName(spec);
        }
        else
        {
            return false;
        }
    }

    bool ProductParams::loadFromIni(ProductParams& params)
    {
        return true;
    }

    bool ProductParams::saveToLocal(const ProductParams& params, const QString path)
    {
        QString fileName = path;

        if (fileName.isEmpty())
        {
            fileName = PLScanner::instance()->productDir + "/" + params.specName + ".plsp";
        }

        Core::ZipWriter writer(fileName);

        if(writer.result() != 0)
        {
            return false;
        }

        QByteArray pdata;
        QDataStream sPdata(&pdata, QIODevice::WriteOnly);
        sPdata << params.specName;
        sPdata << params.materialsType;
        sPdata << params.materialsWidth;
        sPdata << params.materialsUpWidth;
        sPdata << params.materialsThickness;
        sPdata << params.definitionPoints;
        sPdata << params.measureThickness;
        sPdata << params.fixedPoint;
        sPdata << params.measureType;
        writer.addFile("params.dat", pdata);

        QByteArray config;
        QDataStream sConfig(&config, QIODevice::WriteOnly);
        sConfig << params.insParams;
        sConfig << params.config;
        writer.addFile("config.dat", config);

        if(!params.profiles.isEmpty())
        {
            QByteArray profile;
            QDataStream sProfile(&profile, QIODevice::WriteOnly);

            sProfile << params.profiles;

            writer.addFile("profile.dat", profile);
        }

        return true;
    }

    bool ProductParams::saveToSys(const ProductParams& params, const QString name)
    {
        return true;
    }

    bool ProductParams::saveToIni(const ProductParams& params)
    {
        return true;
    }

    void ProductParams::updateParams()
    {
        QMetaObject::invokeMethod(Core::OiGetMainWindow(), "onParamsChanged");
    }

    void ProductParams::updateFormual()
    {
        QMetaObject::invokeMethod(Core::OiGetMainWindow(), "onFormualChanged");
    }

    class productReader_OPC : public ParamsReader
    {
    public:
        productReader_OPC();
        ~productReader_OPC();

    public:
        bool ready();
        bool params(ProductParams&);
        bool specName(QString&);
    };

    productReader_OPC::productReader_OPC()
    {
        OPCTask::instance()->init();
    }

    productReader_OPC::~productReader_OPC()
    {
        OPCTask::instance()->exit();
    }

    bool productReader_OPC::ready()
    {
        return OPCTask::instance()->isRunning();
    }

    bool productReader_OPC::params(ProductParams& params)
    {
        return OPCTask::instance()->readParams(params);
    }

    bool productReader_OPC::specName(QString& name)
    {
        return OPCTask::instance()->readSpecName(name);
    }

    class productReader_SL_PLC : public ParamsReader
    {
    public:
        productReader_SL_PLC();
        ~productReader_SL_PLC();

    public:
        bool ready();
        bool params(ProductParams&);
        bool specName(QString&);
    };

    productReader_SL_PLC::productReader_SL_PLC()
    {
        PLCTask::instance()->init();
    }

    productReader_SL_PLC::~productReader_SL_PLC()
    {
        PLCTask::instance()->exit();
    }

    bool productReader_SL_PLC::ready()
    {
        return PLCTask::instance()->isRunning();
    }

    bool productReader_SL_PLC::params(ProductParams& params)
    {
        return PLCTask::instance()->readParams(params);
    }

    bool productReader_SL_PLC::specName(QString& name)
    {
        return PLCTask::instance()->readSpecName(name);
    }


    QSharedPointer<ParamsReader> ParamsReader::create(QString name)
    {
        if (name == "OPC")
        {
            return  QSharedPointer<ParamsReader>(new productReader_OPC());
        }
        else if (name == "SL_PLC")
        {
            return  QSharedPointer<ParamsReader>(new productReader_SL_PLC());
        }
        return QSharedPointer<ParamsReader>();
    }

    ParamsReaderSystem::ParamsReaderSystem()
    {
        OI_I(ParamsReaderSystem);
    }

    ParamsReaderSystem::~ParamsReaderSystem()
    {
        OI_E(ParamsReaderSystem);

        d->_readers.clear();
    }

    QSharedPointer<ParamsReader> ParamsReaderSystem::reader(QString name)
    {
        OI_F(ParamsReaderSystem);
        if(d->_readers.contains(name))
        {
            return d->_readers[name];
        }
        else
        {
            auto reader = ParamsReader::create(name);
            if(reader)
            {
                d->_readers[name] = reader;
                return reader;
            }

            return QSharedPointer<ParamsReader>();
        }
    }

    void ParamsReaderSystem::clear()
    {
        OI_F(ParamsReaderSystem);
        d->_readers.clear();
    }

    void ParamsReaderSystem::addReader(QString name, QSharedPointer<ParamsReader> reader)
    {
        OI_F(ParamsReaderSystem);

        d->_readers[name] = reader;
    }

    ParamsReaderSystem* ParamsReaderSystem::instance()
    {
        static ParamsReaderSystem system;
        return &system;
    }

    QDataStream & operator<<(QDataStream &stream, const DefinitionPoint dp)
    {
        stream << dp.type
            << dp.distance
            << dp.thicknessLeft
            << dp.thicknessRight
            << dp.radius;

        return stream;
    }

    QDataStream & operator >> (QDataStream &stream, DefinitionPoint& dp)
    {
        stream >> dp.type
            >> dp.distance
            >> dp.thicknessLeft
            >> dp.thicknessRight
            >> dp.radius;

        return stream;
    }

} }
